package com.tastesport.chat.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tastesport.chat.model.redpacket.*;
import com.tastesport.chat.model.redpacket.QueryParam;
import com.tastesport.chat.mapper.RedpacketGetMapper;
import com.tastesport.chat.service.RedpacketService;
import com.tastesport.chat.util.RandomUtil;
import com.tastesport.chat.util.RedisUtil;
import com.tastesport.chat.util.errer.ErrerMsg;
import com.tastesport.chat.util.response.ResponseObj;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.*;

@RestController
@RequestMapping("/api/redpacket")
public class RedpacketController {

    @Autowired
    RedpacketService rpService;

    @Autowired
    RedpacketGetMapper rpgMapper;

    @Autowired
    RedisUtil redis;
    @Value("${custom-attr.redis.rootname}")
    String redisRoot;

    /**
     * 发红包
     *
     * @param param
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/send", method = RequestMethod.POST)
    public ResponseObj send(@RequestBody RedpacketParam param) throws Exception {
        ResponseObj response = verify(param);
        if (response.getCode() < 0) { // 校验发红包数据
            return response;
        }
        Redpacket rp = new Redpacket();
        rp.setUserId(param.getUserId());             // 发红包人id
        BigDecimal money = new BigDecimal(param.getMoney());
        BigDecimal[] predistribution = new BigDecimal[param.getTotalNum()]; // 多红包预分配金额
        if (1 == param.getType()) { // 普通红包
            predistribution = RandomUtil.mathGenearl(money, param.getTotalNum());
            money = money.multiply(new BigDecimal(param.getTotalNum())); // 总金额 = 红包金额 * 总个数
        } else if (2 == param.getType()) { // 拼手气
//            predistribution = RandomUtil.mathRandom(money, param.getTotalNum(), BasesettingProperties.minmoney);
            predistribution = RandomUtil.mathRandom(money, param.getTotalNum(), new BigDecimal(1));
        }
        rp.setTotalMoney(money);
        // 校验用户金币数
        if (checkUsermoney(rp.getUserId(), money)) {
            return ResponseObj.createResponse(-1, "用户金币总数不足");
        }
        rp.setType(param.getType());                 // 红包类型(预留:0、普通:1、拼手气:2)
        rp.setContent(param.getContent());           // 红包文本
        rp.setTotalNum(param.getTotalNum());         // 红包总个数
        rp.setRemainMoney(rp.getTotalMoney());            // 剩余金额 = 总金额
        rp.setRemainNum(rp.getTotalNum());                // 剩余个数 = 总个数
        Date date = new Date();
        rp.setSendTime(date.getTime());                   // 发放时间为当前时间
//        System.out.println("date.getTime():" + date.getTime());
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
//        int expiretime = BasesettingProperties.expiretime;  // 取得设置的过期时间间隔(12小时)
        int expiretime = 12;  // 取得设置的过期时间间隔(12小时)
        cal.add(Calendar.HOUR_OF_DAY, expiretime);
        rp.setExpireTime(cal.getTime().getTime());                   // 过期时间为当前时间+过期时间间隔
        rp.setState(2);                                     // 红包状态(0:待定、1:已领完、2:未领完、3:过期)
//        System.out.println("存储对象为:"+rp.toString());
        int res = rpService.insert(rp); // 插入红包表
        if (res > 0) {
            List tryOpen = new ArrayList<Object>(res);              // 新建“试拆红包”缓存"表"
            redis.set(redisRoot + ":redpacket:tryopen:" + rp.getId(), tryOpen);
        } else {
            return ResponseObj.createErrResponse(ErrerMsg.ERRER600);
        }

        rpService.changeMoney(rp.getUserId(), rp.getTotalMoney().negate()); // 用户数据中扣除金币金额
        Map<String, Object> getRp = new HashMap<String, Object>();
        getRp.put("type", rp.getType());
        getRp.put("userId", rp.getUserId());
        getRp.put("yetNum", 0);                        // 此红包已领个数
        getRp.put("totalNum", rp.getTotalNum());       // 此红包总个数
        getRp.put("predistribution", predistribution);  // 此红包预分配金额
        getRp.put("remainMoney", money);
        getRp.put("totalMoney", money);
        if (2 == rp.getType()) {
            getRp.put("bestUserid", -1);                   // 手气最佳用户id
            getRp.put("bestMoney", new BigDecimal(0)); // 手气最佳金额
        }
//        System.out.println("res:" + res + ",rp:" + rp.getId() + ".");
        // 将新发红包数据存入redis缓存
        redis.set(redisRoot + ":redpacket:send:" + rp.getId(), getRp); // redis对象不用设置过期时间，有专门的定时任务进行清理
        return ResponseObj.createResponse(0, "发红包成功", res);
    }

    /**
     * 抢红包
     *
     * @param param
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/open", method = RequestMethod.POST)
    public ResponseObj open(@RequestBody QueryParam param) throws Exception {
        Map<String, Object> getRp = (Map<String, Object>) redis.get(redisRoot + ":redpacket:send:" + param.getId());
        if (getRp == null) { // redis缓存中取不到该红包数据，证明已领取完或已过期
            // 2020-02-04 Alex
            Map<String, String> map = this.getSendById(param.getId());
            if (null == map) {
                return ResponseObj.createResponse(-5, "出错啦");
            } else if ("3".equals(map.get("state"))) {
                return ResponseObj.createResponse(-2, "红包已过期", map);
            } else if ("1".equals(map.get("state"))) {
                return ResponseObj.createResponse(-1, "手慢了,红包派完了", map);
            }
        }

        // 试拆红包：同一用户不可抢两次，红包剩余个数为零也失败
        int ret = this.tryopen(param);
        System.out.println("试拆红包ret:" + ret);
        Integer type = (Integer) getRp.get("type");
        String lastBestUserid = "" + getRp.get("bestUserid");        // 上次手气最佳用户id
        BigDecimal lastBestMoney = (BigDecimal) getRp.get("bestMoney"); // 上次手气最佳金额
        int totalNum = (Integer) getRp.get("totalNum");
        if (ret == -4) {
            Map<String, String> map = this.getSendById(param.getId());
            return ResponseObj.createResponse(-4, "发红包者不能抢自己发的红包", map); // 20191003发红包者能抢自己发的红包
        } else if (ret == -3) {
            Map<String, String> map = this.getSendById(param.getId());
            return ResponseObj.createResponse(-3, "同一用户同一红包只能抢一次", map);
        } else if (ret == 0) { // 抢到最后一个红包的处理
            redis.del(redisRoot + ":redpacket:tryopen:" + param.getId());
            redis.del(redisRoot + ":redpacket:send:" + param.getId());
            rpService.setState(param.getId(), 1); // 设置红包已领取完
        }
        getRp.replace("yetNum", ret);      // 更新已领个数
        // 抢成功时：从缓存得到所抢金额，更新红包已领个数、手气最佳和金额，更新redis缓存数据，保存抢红包用户数据，
        BigDecimal[] predistribution = (BigDecimal[]) getRp.get("predistribution");
        BigDecimal money = predistribution[ret];                // 从redis缓存获取这个红包的金额
        BigDecimal remainMoney = ((BigDecimal) getRp.get("remainMoney")).subtract(money);
        getRp.put("remainMoney", remainMoney);
        Redpacket rp = new Redpacket();
        rp.setType(type);
        rp.setId(param.getId());
        rp.setRemainMoney(money.negate()); // 剩余金额应减去的金额
//        System.out.println(rp.toString());
        RedpacketGet rpg = new RedpacketGet();
        rpg.setRedpacketId(param.getId());
        rpg.setGetUserid(param.getUserId());
        rpg.setGetMoney(money);
        rpg.setGetTime(new Date().getTime());
        if (type == 2 && money.compareTo(lastBestMoney) == 1) { // 如果金额大于手气最佳
            getRp.replace("bestUserid", param.getUserId());     // 更新手气最佳者
            getRp.replace("bestMoney", money);                 // 更新手气最佳金额
            rp.setUserId(lastBestUserid); // ！！特殊处理：这里临时用来保存上次手气最佳用户id
            rp.setBestUserid(param.getUserId());
            rp.setBestMoney(money);
            rpg.setIsbest(1);
        }
        if (ret != 0) { // 不是最后一个红包则将更新的数据存入redis缓存
            redis.set(redisRoot + ":redpacket:send:" + param.getId(), getRp);
        }
        new Thread() {
            public void run() {
                //子线程去干自己的事情,主线程不等待
                rpService.databaseProc(rp, rpg); // 数据库处理
            }
        }.start();

        Map<String, String> map = this.getSendById(param.getId());
        map.put("money", "" + money);
        return ResponseObj.createResponse(0, "恭喜你，抢到了红包", map);
    }

    // 试拆红包：同一用户不可抢两次，红包剩余个数为零也失败
    private synchronized Integer tryopen(QueryParam param) {
        Map<String, Object> getRp = (Map<String, Object>) redis.get(redisRoot + ":redpacket:send:" + param.getId());
        if (getRp == null) { // redis缓存中取不到该红包数据，证明已领取完或已过期
            return -1;
        } else { // 20191003发红包者能抢自己发的红包
//            Integer userId = (Integer) getRp.get("userId");
//            if (param.getUserId() == userId){
//                return -4; // 发红包人不能抢自己的红包
//            }
        }
        int yetNum = (Integer) getRp.get("yetNum");
        int totalNum = (Integer) getRp.get("totalNum");
        yetNum++;
        if (yetNum == totalNum) {
            yetNum = 0; // 抢到了最后一个红包
        } else if (yetNum > totalNum) {
            return -1; // 红包已抢完了
        }
        List<String> tryOpen = (List<String>) redis.get(redisRoot + ":redpacket:tryopen:" + param.getId());
        if (tryOpen == null || tryOpen.size() == 0) { // 第一个抢红包的人
            tryOpen = new ArrayList<String>();
        } else {
            for (String userId : tryOpen) {
                if (userId.equals(param.getUserId())) {
                    return -3; // 同一用户同一红包只能抢一次
                }
            }
        }
        tryOpen.add(param.getUserId());
        redis.set(redisRoot + ":redpacket:tryopen:" + param.getId(), tryOpen);
//        getRp.replace("yetNum", yetNum);      // 更新已领个数
//        redis.set(redisRoot + ":redpacket:send:" + param.getId(), getRp);
        return yetNum; // 返回是第几个抢成功的人
    }

    private Map<String, String> getSendById(Integer id) {
        RedpacketVO rp = rpService.getRedpacketById(id);
        Map<String, String> map = new HashMap<>();
        map.put("state", "" + rp.getState()); // 红包状态 0:待定、1:已领完、2:未领完、3:过期
        map.put("sendMoney", "" + rp.getTotalMoney()); // 红包总金额
        map.put("redPacketCount", "" + rp.getTotalNum()); // 红包总个数
        map.put("sendPacketUser", rp.getUserName()); // 发红包人昵称
        map.put("profilePicture", rp.getUserIcon()); // 发红包人头像
        map.put("money", "0"); // 抢得红包金额
        return map;
    }

    /**
     * 红包和领取详情
     *
     * @param id
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/{id}", method = {RequestMethod.POST, RequestMethod.GET})
    public ResponseObj show(@PathVariable Integer id) throws Exception {
        Map<String, Object> data = new HashMap<>();
//        RedpacketVO redpacket = rpService.getById(id);
//        data.put("redpacket", redpacket);
        List<RedpacketgetVO> list = rpgMapper.selectList(id);
//        data.put("data", list);
        return ResponseObj.createResponse(0, "红包和领取详情", list);
    }

    @RequestMapping(value = "/usermoney/{userId}", method = {RequestMethod.POST, RequestMethod.GET})
    public ResponseObj usermoney(@PathVariable String userId) throws Exception {
        BigDecimal money = rpService.getMoney(userId);
        if (money != null) {
            return ResponseObj.createResponse(0, money.toString());
        } else {
            return ResponseObj.createResponse(-1, "无此用户");
        }
    }

    private Boolean checkUsermoney(String userId, BigDecimal money) {
        BigDecimal gold_coin = rpService.getMoney(userId); // 取得用户金币总额
        // 如果红包总金额>用户金币总数
        if (money.compareTo(gold_coin) == 1) {
            return true;
        }
        return false;
    }

    /**
     * 用户抢得的红包列表
     *
     * @param query
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/userget", method = RequestMethod.POST)
    public ResponseObj userGet(@RequestBody QueryParam query) throws Exception {
        System.out.println("query:" + query);
        Page page = new Page();
        if (query.getOffset() != null) {
            page.setCurrent(query.getOffset());
        }
        if (query.getPagesize() != null) {
            page.setSize(query.getPagesize());
        }
        IPage<RedpacketgetVO> list = rpService.userGet(page, query);
        return ResponseObj.createResponse(0, "查询成功", list);
    }

    /**
     * 用户发出的红包列表
     *
     * @param query
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/usersend", method = RequestMethod.POST)
    public ResponseObj userSend(@RequestBody QueryParam query) throws Exception {
        System.out.println("query:" + query);
        Page page = new Page();
        if (query.getOffset() != null) {
            page.setCurrent(query.getOffset());
        }
        if (query.getPagesize() != null) {
            page.setSize(query.getPagesize());
        }
        IPage<Redpacket> list = rpService.userSend(page, query);
        return ResponseObj.createResponse(0, "查询成功", list);
    }

    // 校验发红包数据
    private ResponseObj verify(RedpacketParam rp) {
//        double minmoney = BasesettingProperties.minmoney.doubleValue();
        double minmoney = 1d;
        if (rp.getUserId() == null || rp.getType() == null || rp.getTotalNum() == null || rp.getMoney() == null ||
                rp.getContent() == null) {
            return ResponseObj.createResponse(-1, "数据格式不正确");
        } else if (rp.getTotalNum() < 1) {
            return ResponseObj.createResponse(-2, "红包个数至少应为1");
        } else if (Double.parseDouble(rp.getMoney()) < rp.getTotalNum() * minmoney) {
            return ResponseObj.createResponse(-3, "红包金额太小了");
        } else if ("".equals(rp.getContent())) {
            return ResponseObj.createResponse(-4, "写几句祝福语吧");
        }
        return ResponseObj.createResponse(0, "校验成功");
    }
}
